package org.ricks.ioc.bean.factory;

import org.ricks.common.db.*;
import org.ricks.common.bean.ArrayUtil;
import org.ricks.event.*;
import org.ricks.ioc.*;
import org.ricks.ioc.bean.BeanDefinition;
import org.ricks.ioc.bean.BeanReference;
import org.ricks.ioc.bean.PropertyDefinition;
import org.ricks.log.Logger;
import org.ricks.utils.ClassUtil;
import org.ricks.utils.ReflectUtil;
import org.ricks.utils.StrUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author chenwei
 * @Title:
 * @Package
 * @Description:
 * @date 2020/6/2815:50
 */
public abstract class AbstractBeanFactory implements BeanFactory {

    // 用来存储实例化后的bean
    private static final List<Class> daoClazz = new ArrayList<>();
    private static final Map<String, BeanDefinition> beans = new ConcurrentHashMap<>();
    private final Map<Class<? extends Annotation>,List<Object>>  beansForAnnotation = new ConcurrentHashMap<>();

    public void addBeanDefinition(Class<?> clazz) {
        if (clazz.isAnnotationPresent(Bean.class)) {
            initBean(clazz,clazz.getAnnotation(Bean.class).value());
        }
        if(clazz.isAnnotationPresent(Table.class)) {
            daoClazz.add(clazz);
        }
        if (ClassUtil.isAssignable(DaoSourceManage.class,clazz)) {
            PooledMgr.init((Class<? extends DaoSourceManage>) clazz);
        }

//        else if(clazz.isAnnotationPresent(Filter.class)) {
//            initWebFilter(clazz);
//        } todo
    }

    private void initBean(Class clazz, String value) {
        if(StrUtil.isBlank(value)) value = StrUtil.lowerFirst(clazz.getSimpleName());
        createBeanDefinition(value,clazz);
    }

//    private void initWebFilter(Class clazz) {
//        Filter handlerAnn = (Filter) clazz.getAnnotation(Filter.class);
//        Logger.info("{}优先级：{}", clazz.getCanonicalName(), handlerAnn.value());
//        FilterAdapter obj;
//        try {
//            obj = (FilterAdapter) clazz.getDeclaredConstructor().newInstance();
//            Map<String, FilterAdapter> filterMap = new HashMap<>();
//            filterMap.put(clazz.getName(), obj);
//            FilterChain.FILTERS_IOC.add(filterMap);
//        } catch (Exception e) {
//            Logger.error("初始化 {} 错误:{}", clazz.getSimpleName(), e);
//        }
//    } todo

    private void initDao(Class clazz) {
        Table table = (Table) clazz.getAnnotation(Table.class);
        BaseDao baseDao;
        if(table.db().equals(RepositoryDao.class)){
            baseDao = new RepositoryDao(clazz);
        } else {
            baseDao = new AsyncRepositoryDao(clazz,table.delayTime());
        }
        DaoContext.put(clazz,baseDao);
    }

    public List<Object> getBeansForAnnotation(Class<? extends Annotation> annotationType) {
        List<Object> result = new ArrayList<>();
        for (BeanDefinition beanDefinition : beans.values()) {
            if (beanDefinition.getBeanClass().isAnnotationPresent(annotationType)) result.add(beanDefinition.getBean());
        }
        return result;
    }

    private void createBeanDefinition(String defaultName,Class clazz){
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setBeanClass(clazz);
        beans.put(defaultName,beanDefinition);
        processProperty(beanDefinition);
    }

    private void processProperty(BeanDefinition beanDefinition) {
        Field[] fields = beanDefinition.getBeanClass().getDeclaredFields();
        for(Field field : fields){
            if (field.isAnnotationPresent(Autowired.class)) {
                //获取属性的beanid
                String beanId = field.getName();
                String value = field.getAnnotation(Autowired.class).value();
                if(!value.isBlank()) beanId = value;
                BeanReference beanReference = new BeanReference(beanId);
                beanDefinition.addPropertyDefinition(new PropertyDefinition(beanId,beanReference));
            }
        }
    }

    private Object createBeanInstance(BeanDefinition beanDefinition) {
        Object bean = null;
        try {
            bean = beanDefinition.getBeanClass().getDeclaredConstructor().newInstance();
            if(bean instanceof Init) ReflectUtil.invoke(bean,"init");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }

    protected Object doCreateBean(BeanDefinition beanDefinition) {
        Object bean = createBeanInstance(beanDefinition);
        beanDefinition.setBean(bean);
        try {
            applyPropertyValues(bean, beanDefinition);
        } catch (Exception e) {

        }
        return bean;
    }

    protected void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception {

    }

    @Override
    public Object getBean(String beanId) {
        BeanDefinition beanDefinition = beans.get(beanId);
        Object bean = beanDefinition.getBean();
        if (bean == null) {
            bean = doCreateBean(beanDefinition);
            bean = initializeEvent(bean, beanId);
            beanDefinition.setBean(bean);
        }
        return bean;
    }

    public List<Object> getBeans() {
        return beans.values().stream().map(beanDefinition -> beanDefinition.getBean()).collect(Collectors.toList());
    }

    @Override
    public <T> T getBean(Class<T> clazz) {
        String beanName = StrUtil.lowerFirst(clazz.getSimpleName());
        BeanDefinition beanDefinition = beans.get(beanName);
        Object bean = null;
        if(beanDefinition != null) bean = beanDefinition.getBean();
        if (bean == null) {
            bean = doCreateBean(beanDefinition);
            beanDefinition.setBean(bean);
        }
        return (T) bean;
    }

    /**
     * @Description: 实例化 bean
     * @author chenwei
     * @date 2020/10/13 19:38
     */
    public void instanceBeans() {
        // 对个bean进行实例化
        for ( Map.Entry<String, BeanDefinition> entry : beans.entrySet()) {
            getBean(entry.getKey());
        }
    }


    private Object initializeEvent(Object bean, String beanName) {
        var clazz = bean.getClass();
        var methods = ReflectUtil.getMethodsByAnnoInPOJOClass(clazz, EventHandler.class);
        if (ArrayUtil.isEmpty(methods)) {
            return bean;
        }

        if (clazz.getSuperclass().equals(Object.class)) {
            Logger.warn("事件注册类[{}]不是POJO类，父类的事件接收不会被扫描到", clazz);
        }

        try {
            for (var method : methods) {
                var paramClazzs = method.getParameterTypes();
                if (paramClazzs.length != 1) {
                    throw new IllegalArgumentException(StrUtil.format("[class:{}] [method:{}] must have one parameter!", bean.getClass().getName(), method.getName()));
                }
                if (!IEvent.class.isAssignableFrom(paramClazzs[0])) {
                    throw new IllegalArgumentException(StrUtil.format("[class:{}] [method:{}] must have one [IEvent] type parameter!", bean.getClass().getName(), method.getName()));
                }

                var eventClazz = (Class<? extends IEvent>) paramClazzs[0];
                var eventName = eventClazz.getCanonicalName();
                var methodName = method.getName();

                if (!Modifier.isPublic(method.getModifiers())) {
                    throw new IllegalArgumentException(StrUtil.format("[class:{}] [method:{}] [event:{}] must use 'public' as modifier!", bean.getClass().getName(), methodName, eventName));
                }

                if (Modifier.isStatic(method.getModifiers())) {
                    throw new IllegalArgumentException(StrUtil.format("[class:{}] [method:{}] [event:{}] can not use 'static' as modifier!", bean.getClass().getName(), methodName, eventName));
                }

                var expectedMethodName = StrUtil.format("on{}", eventClazz.getSimpleName());
//                if (!methodName.equals(expectedMethodName)) {
//                    throw new IllegalArgumentException(StrUtil.format("[class:{}] [method:{}] [event:{}] expects '{}' as method name!"
//                            , bean.getClass().getName(), methodName, eventName, expectedMethodName));
//                }

                var receiverDefinition = new EventReceiverDefinition(bean, method, eventClazz);
//                var enhanceReceiverDefinition = EnhanceUtils.createEventReceiver(receiverDefinition); //动态代理 避免反射，这块看看如何ASM优化

                // key:class类型 value:观察者 注册Event的receiverMap中
                EventBus.registerEventReceiver(eventClazz, receiverDefinition);
            }
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }

        return bean;
    }

    public void initDao() {
        daoClazz.forEach(aClass -> initDao(aClass));
    }
}
